package backend;

import java.util.LinkedList;
import java.util.Queue;
import java.util.Random;

import backend.Cpu.Interrupt;
import backend.Cpu.Interrupt_Handle;

/**
 * IO.java
 * 
 * Purpose is to conserve code for simulating IO devices.
 * 
 * @author BitWiseGuys Dallas Rosson, Joshua Warner, Tyson Lewis, Ken Norton
 * @version 1.0, 5/29/2012
 */
public class IO {
	
	/**
	 * The processes waiting for requests to be run.
	 */
	private Queue<PCB> my_wait_queue;
	
	/**
	 * the threads representing requests to be processed.
	 */
	private Queue<Thread> my_request_queue;
	
	/**
	 * the request currently being worked.
	 */
	private Thread current_request;
	
	/**
	 * reference to CPU.
	 */
	private final Cpu MY_CPU;
	
	/**
	 * max time for an io request.
	 */
	private final int MAX_WAIT = 1000;
	
	/**
	 * the type of IO device this is.
	 */
	private IOType my_inturrupt_numb;
	
	/**
	 * Enum of the different types of possible IO Devices.
	 */
	public enum IOType{
		KEYBOARD, MONITOR, DISK, SHARE_MEM_WRITE, SHARE_MEM_READ;
	}
	
	/**
	 * Runnable subclass for to simulate the time it would 
	 * take to execute a request and then start the next request.
	 */
	private class Request implements Runnable{
		int request_time;
		
		/**
		 * set the time to execute the request.
		 * @param the_time to execute request.
		 */
		public void setRequestTime(int the_time){
			if (the_time > 0){
				request_time = the_time;
			}else{
				request_time = 1;
			}
		}
		
		@Override
		public void run() {
			try{
				Thread.sleep(request_time);
			}catch(InterruptedException e){
				System.out.println("IO thread died.");
			}
			
			while(MY_CPU.getInterruptState() == Cpu.Interrupt.DISABLED){
				try{
					Thread.sleep(1);
				}catch(Exception e){
					System.out.println("thread wait died.");
				}
				
			}
			MY_CPU.setInterruptHandle(Interrupt_Handle.IO_INT_HANDLE);
			MY_CPU.setIo_type(my_inturrupt_numb);
			
			System.out.println("current_request: " + current_request);
			if (!my_request_queue.isEmpty()){
				current_request = my_request_queue.remove();
				current_request.start();
			}else{
				current_request = null;
			}
			System.out.println("current_request: " + current_request);
		}
	}
	
	/**
	 * 
	 * @param io_type :0-no IO, 1-Timer, 2-video, 3-Disk, 4-KeyBoard
	 */
	public IO(IOType io_type, Cpu the_cpu){		
		MY_CPU = the_cpu;
		my_inturrupt_numb = io_type;
		my_wait_queue = new LinkedList<PCB>();
		my_request_queue = new LinkedList<Thread>();
	}
	
	/**
	 * add the given PCB to this IOs wait queue and simulate a request made.
	 */
	public void addRequest(PCB the_process){
		int request_time;
		Random rand = new Random();
		request_time = rand.nextInt(MAX_WAIT);
		Request r = new Request();
		r.setRequestTime(request_time);
		Thread t = new Thread(r);
		
		my_wait_queue.add(the_process);

		if(current_request == null){
			current_request = t;
			t.start();
		}else{
			my_request_queue.add(t);
		}	
	}
	
	/**
	 * remove the current process from the queue.
	 * @return the process removed.
	 */
	public PCB getPCB(){
		return my_wait_queue.remove();
	}
	
	/**
	 * Tells if the IO waiting queue is empty.
	 * @return true if queue is empty.
	 */
	public boolean waitQueueIsEmpty(){
		return my_wait_queue.isEmpty();
	}
	
	/**
	 * creates a string with the process id's waiting for this IO
	 * @return the String of ids.
	 */
	public String getWaitinglist(){
		StringBuffer sb = new StringBuffer();
		String output = "";
		if (my_wait_queue != null){
			for(PCB pcb: my_wait_queue){
				sb.append(pcb.getPid() + ", ");
			}
			if (sb.length() > 2){
				output = sb.substring(0, sb.length() - 2);
			}
		}
		
		return output;
	}
}
